<?php
/**
 * [LupMisNotAllowedSync]
 * 本代码为系统自动生成代码，请根据自己业务进行修改;
 * 生成时间 2022-02-01 22:12:12;
 * 版权所有 2020-2022 lizhongwen，并保留所有权利;
 * 说明: 这不是一个自由软件！您只能在不用于商业目的的前提下对程序代码进行修改和使用；不允许对程序代码以任何形式任何目的的再发布;
 * 作者: 中文Lee;
 * 作者主页: http://www.lizhongwen.com;
 * 邮箱: 360811363@qq.com;
 * QQ: 360811363;
 */

namespace application\core;

use DOMDocument;
use DOMXPath;
use Exception;

class Mapper
{

    public static $obj;

    public static $mapper;

    public static $id;

    public $xml;

    public $xpath;

    public $sql;

    public $sqlCount;

    public $attr;

    public $query;

    public static function map($mapper = "", $id = ""): Mapper
    {

        if (!empty($id)) {
            self::$id = $id;
        }
        if (!empty($mapper)) {
            self::$mapper = $mapper;
        }
        if (self::$obj !== null) {
            return self::$obj;
        } else {
            return self::$obj = new self();
        }

    }


    public function exec($data = [], $pagePars = [], $link = 'default')
    {
        if (!$this->xml) {
            $this->xml = new DOMDocument('1.0', 'UTF-8');
        }
        $mapperPath = APP_PATH . 'resources/mapper/' . self::$mapper . '.xml';
        if (file_exists($mapperPath)) {
            try {
                //$xmlData = file_get_contents($mapperPath);
                //$bool = $this->xml->loadXML($xmlData);
                $bool = $this->xml->load($mapperPath);
                if(!$bool){
                    $this->xml = "";
                    return Result::fail(false,[], 'xml解析错误');
                }
            }catch (Exception $e){
                $this->xml = "";
                return Result::fail(false,[], 'xml解析错误'.$e->getMessage());
            }
        } else {
            return Result::fail(false,[], '没有找到xml配置文件');
        }
        try {
            $this->xpath = new DOMXPath($this->xml);
        }catch (Exception $e){
            return Result::fail(false,[], 'xml解析错误'.$e->getMessage());
        }
        $id = "//*[@id = '" . self::$id . "']";
        $entries = $this->xpath->query($id);
        if ($entries->length == 0) {
            return Result::fail(false,[], '没有找到要执行的SQL语句');
        }
        if ($entries->item(0)->attributes->item(1)->nodeValue) {
            $this->attr = $entries->item(0)->attributes->item(1)->nodeValue;
        }
        $this->query = $entries->item(0)->nodeName;
        // 查找是否有局部变量
        $fields = '';
        $fieldsXml = $entries->item(0)->getElementsByTagName("fields");
        if ($fieldsXml->length) {
            $fields = $fieldsXml->item(0)->nodeValue;
            $fieldsXml->item(0)->nodeValue = '#FIELDS#';
        }

        // if条件判断start
        $if = $entries->item(0)->getElementsByTagName("if");

        if ($if->length) {
            foreach ($if as $kIf => $vIf) {
                unset($vIf);
                $eq = $if->item($kIf)->getAttribute("test");
                $_tmpEq = str_replace("}", "']", $eq);
                $_tmpEq = str_replace("{:", '$' . "data['", $_tmpEq);
                $p = '/{:([^^]*?)}/i';
                $matches = [];
                preg_match_all($p, $eq, $matches);
                foreach ($matches[1] as $_fd) {
                    $_fd = trim($_fd);
                    if (!isset($data[$_fd])) {
                        $if->item($kIf)->nodeValue = '';
                    } else {
                        $bool = eval(sprintf("return %s;", $_tmpEq));
                        if (!$bool) {
                            $if->item($kIf)->nodeValue = '';
                        }
                    }
                }
            }
        }
        // if条件判断end
        // exit;
        // foreach判断start
        $foreach = $entries->item(0)->getElementsByTagName("foreach");
        if ($foreach->length) {
            foreach ($foreach as $kForeach => $vForeach) {
                unset($vForeach);
                // 取出from的值
                $fromVal =  $foreach->item($kForeach)->getAttribute("from");
                $item = $foreach->item($kForeach)->getAttribute("item");
                if($item){
                    $separator = $foreach->item($kForeach)->getAttribute("separator");
                    $split = $foreach->item($kForeach)->getAttribute("split");
                    //循环
                    $foreachValue = $foreach->item($kForeach)->nodeValue;
                    $_arr = [];
                    $guid = 0;
                    $_valFd = str_replace("{:","",$fromVal);
                    $_valFd = str_replace("}","",$_valFd);
                    $vals = $data[$_valFd];
                    if(!isset($split)){
                        $split = ",";
                    }
                    $valsArr = explode("$split",$vals);
                    foreach ($valsArr as $v) {
                        $_bind = $_valFd . '_lzw_' . $guid . '_wzl_' . $_valFd;
                        $fdBind[] = ':' . $_bind;
                        $data[$_bind] = $v;
                        //find_in_set(:id,role_ids)
                        $_arr[] = str_replace(":".$item,":".$_bind,$foreachValue);
                        ++$guid;
                        unset($v);
                    }
                    if(empty($separator)){
                        $separator = "or";
                    }
                    $foreach->item($kForeach)->nodeValue = implode(" $separator ",$_arr);
                }else{
                    $p = '/{:([^^]*?)}/i';
                    $matches = [];
                    preg_match($p, $fromVal, $matches);
                    $fd1 = $matches[1];
                    $guid = 0;
                    $dataArr = $data[$fd1];
                    $fdBind = [];
                    if (!is_array($dataArr)) {
                        $dataArr = explode(',', $dataArr);
                    }
                    if (strpos($foreach->item($kForeach)->nodeValue, ":?")) {
                        if (!empty($dataArr)) {
                            foreach ($dataArr as $v) {
                                $_bind = $fd1 . '_lzw_' . $guid . '_wzl_' . $fd1;
                                $fdBind[] = ':' . $_bind;
                                $data[$_bind] = $v;
                                ++$guid;
                                unset($v);
                            }
                        }
                        //移除掉原来的变量
                        unset($data[$fd1]);
                        $foreach->item($kForeach)->nodeValue = str_replace(':?', implode(',', $fdBind), $foreach->item($kForeach)->nodeValue);
                    }
                }
            }
        }
        // foreach判断end

        // where判断start
        $where = $entries->item(0)->getElementsByTagName("where");
        if ($where->length) {
            $whereValue = $where->item(0)->nodeValue;
            $whereValue = preg_replace("/(\s+)/", ' ', trim($whereValue));
            if (!empty($whereValue)) {
                $and = substr(trim($whereValue), 0, 3);
                $or = substr(trim($whereValue), 0, 2);
                if ('AND' == strtoupper($and)) {
                    $where->item(0)->nodeValue = 'WHERE ' . substr(trim($whereValue), 3);
                } elseif ('OR' == strtoupper($or)) {
                    $where->item(0)->nodeValue = 'WHERE ' . substr(trim($whereValue), 2);
                } else {
                    $where->item(0)->nodeValue = 'WHERE ' . $whereValue;
                }
            }
        }
        // where判断end

        // set判断start
        $set = $entries->item(0)->getElementsByTagName("set");

        if ($set->length) {
            foreach ($set as $kSet => $vSet) {
                $setValue = trim($set->item($kSet)->nodeValue);
//                $setValue = trim($set->item(0)->nodeValue);
                if ('UPDATE' == strtoupper($this->query) && $setValue == '') {
                    return Result::fail(false,[], '没有要修改的值');
                }
                if ('INSERT' == strtoupper($this->query) && $setValue == '') {
                    return Result::fail(false,[], '没有要添加的值');
                }
                if ('UPDATE' == strtoupper($this->query)) {
                    if (',' == substr(trim($setValue), -1)) {
                        $setValue = rtrim(trim($setValue), ',');
                    }
                    $set->item($kSet)->nodeValue = str_replace($setValue, ' SET ' . trim($setValue), $setValue);
                }
                if ('INSERT' == strtoupper($this->query)) {
                    if (',' == substr(trim($setValue), -1)) {
                        $setValue = rtrim(trim($setValue), ',');
                    }
                    $set->item($kSet)->nodeValue = str_replace($setValue, '  ' . trim($setValue), $setValue);
                }
//                $set->item(0)->nodeValue = str_replace($setValue, ' SET ' . trim($setValue), $setValue);
            }
        }
        // set判断end

        // order by 判断start
        $order = $entries->item(0)->getElementsByTagName("order");
        if ($order->length) {
            // 自定义排序
            $sortField = $pagePars['lup_sort_field'];
            $sortOrder = $pagePars['lup_sort_order'];
            if ($sortField == 'system_serial_number') {
                $sortField = "id";
            }
            if (!empty($sortField) && !empty($sortOrder)) {
                $orderValue = $sortField . ' ' . $sortOrder;
            } else {
                $orderValue = $order->item(0)->nodeValue;
            }
            $order->item(0)->nodeValue = '';
            $this->sqlCount = $entries->item(0)->nodeValue;
            $order->item(0)->nodeValue = 'ORDER BY ' . $orderValue;
        } else {
            $this->sqlCount = $entries->item(0)->nodeValue;
        }
        $this->sql = $entries->item(0)->nodeValue;
        //查询是否有全局table
        $tableXml = $this->xml->getElementsByTagName("table");
        if ($tableXml->length) {
            if (strpos($this->sql, '#table#')) {
                $this->sql = str_replace('#table#', $tableXml->item(0)->nodeValue, $this->sql);
                $this->sqlCount = str_replace('#table#', $tableXml->item(0)->nodeValue, $this->sqlCount);
            }
        }
        if (empty($fields)) {
            // 查找全局字段
            $fieldsXml = $this->xml->getElementsByTagName("fields");
            if ($fieldsXml->length) {
                $fieldsXml->item(0)->nodeValue;
                if (strpos($this->sql, '#fields#')) {
                    $this->sql = str_replace('#fields#', $fieldsXml->item(0)->nodeValue, $this->sql);
                    $this->sqlCount = str_replace('#fields#', ' COUNT(0) ', $this->sqlCount);
                } else {
                    $p = '/SELECT([^^]*)FROM/i';
                    $matches = [];
                    preg_match($p, $this->sqlCount, $matches);
                    $fd1 = $matches[1];
                    if ('#FIELDS#' != strtoupper($fd1)) {
                        $this->sqlCount = str_replace($fd1, ' COUNT(0) ', $this->sqlCount);
                    }
                }
            } else {
                $p = '/SELECT([^^]*)FROM/i';
                $matches = [];
                preg_match($p, $this->sqlCount, $matches);
                $fd1 = $matches[1];
                if ('#FIELDS#' != strtoupper($fd1)) {
                    $this->sqlCount = str_replace($fd1, ' COUNT(0) ', $this->sqlCount);
                }
            }
        } else {
            if (strpos($this->sql, '#FIELDS#')) {
                $this->sql = str_replace('#FIELDS#', trim($fields), $this->sql);
                $this->sqlCount = str_replace('#FIELDS#', ' COUNT(0) ', $this->sqlCount);
            }
        }
        //判断sql中是否有变量，如果有变量则替换变量信息
        $p = '/{\$([^^]*?)}/i';
        $matches = [];
        preg_match_all($p, $this->sql, $matches);
        $variableNameArr = $matches[1];
        foreach ($variableNameArr as $_v) {
            $this->sql = str_replace('{$' . $_v . '}', $data[$_v], $this->sql);
        }
        $p = '/{\$([^^]*?)}/i';
        $matches = [];
        preg_match_all($p, $this->sqlCount, $matches);
        $variableNameArr = $matches[1];
        foreach ($variableNameArr as $_v) {
            $this->sqlCount = str_replace('{$' . $_v . '}', $data[$_v], $this->sqlCount);
        }
        return self::query($data, $pagePars, $link);
    }

    public function byId($id): Mapper
    {
        self::$id = $id;
        return $this;
    }

    /**
     * 执行查询
     *
     * @param array $data
     * @param array $pagePars
     * @param string $link
     * @return NULL|mixed|array|number
     * @throws Exception
     */
    public function query(array $data = [], array $pagePars = [], string $link = 'default')
    {
        $ret = [];
        if ($this->query == 'insert') {
            $sth = Db::query($this->sql, $data, $link);
            $ret = $sth->rowCount();
        } else if ($this->query == 'delete') {
            $sth = Db::query($this->sql, $data, $link);
            $ret = $sth->rowCount();
        } else if ($this->query == 'update') {
            $sth = Db::query($this->sql, $data, $link);
            $ret = $sth->rowCount();
        } else if ($this->query == 'select') {
            if ($this->attr == 'fetch') {
                $ret = Db::fetch($this->sql, $data, $link);
                if (empty($ret)) {
                    $ret = [];
                }
            } else if ($this->attr == 'fetchColumn') {
                try {
                    $ret = Db::fetchColumn($this->sql, $data, $link);
                } catch (Exception $e) {
                    Result::fail(false,['error' => $e]);
                }
            } else {
                if (!empty($pagePars)) {
                    $limit = $pagePars['limit'] ?? 20;
                    $page = $pagePars['page'] ?? 1;
                    $totalCount = Db::fetchColumn($this->sqlCount, $data, $link);
                    $pageCount = ceil($totalCount / $limit);
                    $limit = ($limit <= 1) ? 1 : $limit;
                    $page = ($page >= $pageCount) ? $pageCount : $page;
                    $isFirstPage = ($page <= 1) ? 1 : -1;
                    $isLastPage = ($page >= $pageCount) ? 1 : -1;
                    $start = intval($page - 1) * $limit;
                    if ($start <= 0) {
                        $start = 0;
                    }
                    $data['limitStart'] = $start;
                    $data['limitEnd'] = intval($limit);
                    $this->sql .= ' LIMIT :limitStart,:limitEnd';
                    $rows = Db::fetchAll($this->sql, $data, $link);
                    if (empty($rows)) {
                        $rows = [];
                    }
                    $ret = [
                        'page' => intval($page),
                        'limit' => intval($limit),
                        'total' => intval($totalCount),
                        'page_count' => intval($pageCount),
                        'is_first_page' => $isFirstPage,
                        'is_last_page' => $isLastPage,
                        'rows' => $rows
                    ];
                } else {
                    $ret = Db::fetchAll($this->sql, $data, $link);
                    if (empty($ret)) {
                        $ret = [];
                    }
                }
            }
        }
        return $ret;
    }

    public static function mapKey($fd, $data): array
    {
        $array = [];
        $i = 0;
        foreach ($data as $v) {
            unset($v);
            $array[$i] = ':' . $fd . '_lzw_' . $i++ . '_wzl_' . $fd;
            ++$i;
        }
        return $array;
    }


    public static function beginTransaction($link = 'default'): bool
    {
        return Db::beginTransaction($link);
    }

    public static function inTransaction($link = 'default'): bool
    {
        return Db::inTransaction($link);
    }

    public static function rollBack($link = 'default'): bool
    {
        return Db::rollBack($link);
    }

    public static function commit($link = 'default'): bool
    {
        return Db::commit($link);
    }
}
